import sys
import os
import re
import requests
import json
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QListWidget,
    QVBoxLayout, QWidget, QFileDialog, QProgressBar, QHBoxLayout,
    QStyledItemDelegate, QGraphicsOpacityEffect, QListWidgetItem, QMessageBox
)
from PySide6.QtCore import (
    Qt, QSettings, QPropertyAnimation, QEasingCurve,
    QParallelAnimationGroup, QThread, Signal, QObject
)
from PySide6.QtGui import QFont, QColor, QPalette
import time
from functools import lru_cache
from PySide6.QtCore import Qt, QTimer
import pickle


class WorkerSignals(QObject):
    finished = Signal()
    error = Signal(str)
    data_loaded = Signal(list)
    download_progress = Signal(int)
    download_complete = Signal(str)
    url_obtained = Signal(str)


class NetworkWorker(QThread):
    def __init__(self, url, force_refresh=False):
        super().__init__()
        self.url = url
        self.force_refresh = force_refresh
        self.signals = WorkerSignals()

    def run(self):
        try:
            response = requests.get(self.url, timeout=10)
            response.raise_for_status()
            data = response.json()
            self.signals.data_loaded.emit(data["data"]["list"])
        except Exception as e:
            self.signals.error.emit(f"网络请求失败: {str(e)}")
        finally:
            self.signals.finished.emit()


class DownloadWorker(QThread):
    def __init__(self, url, save_path):
        super().__init__()
        self.url = url
        self.save_path = save_path
        self.signals = WorkerSignals()

    def run(self):
        try:
            response = requests.get(self.url, stream=True)
            response.raise_for_status()
            total_size = int(response.headers.get('content-length', 0))
            block_size = 8192
            downloaded = 0

            # 发送总大小信号（使用负数表示设置最大值）
            self.signals.download_progress.emit(-total_size)

            with open(self.save_path, "wb") as file:
                for chunk in response.iter_content(chunk_size=block_size):
                    if chunk:
                        file.write(chunk)
                        downloaded += len(chunk)
                        self.signals.download_progress.emit(downloaded)
                        if self.isInterruptionRequested():
                            return

            self.signals.download_complete.emit(self.save_path)
        except Exception as e:
            self.signals.error.emit(f"下载失败: {str(e)}")
        finally:
            self.signals.finished.emit()


class UrlFetchWorker(QThread):
    def __init__(self, html_url):
        super().__init__()
        self.html_url = html_url
        self.signals = WorkerSignals()

    def run(self):
        try:
            response = requests.get(self.html_url)
            if response.status_code == 200:
                html = response.text
                pattern = r'"sourceUrl":"([^"]+\.\w+)"'
                matches = re.findall(pattern, html)
                if matches:
                    encoded_string = matches[0]
                    decoded_string = encoded_string.replace("\\\\", "/")
                    decoded_string = decoded_string.replace("\\u002F", "/")
                    self.signals.url_obtained.emit(decoded_string)
                else:
                    self.signals.error.emit("未找到下载URL")
            else:
                self.signals.error.emit(f"请求失败，状态码：{response.status_code}")
        except Exception as e:
            self.signals.error.emit(f"发生错误：{str(e)}")
        finally:
            self.signals.finished.emit()


class MusicDownloader(QMainWindow):
    def __init__(self):
        super().__init__()
        self.load_cache_from_disk()
        self.cache_expire_time = 3600  # 缓存过期时间(秒)
        self.last_fetch_time = 0  # 上次获取时间戳
        self.songs_cache = None  # 缓存数据
        self.current_download_worker = None
        self.setup_ui()
        self.setup_animations()
        self.setup_connections()
        self.msr_json("https://monster-siren.hypergryph.com/api/songs")

    def setup_ui(self):
        self.setWindowTitle("MONSTER SIREN 音乐下载器")
        self.setFixedSize(550, 650)

        # 主窗口样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f5f7fa;
            }
            QPushButton {
                background-color: #4a90e2;
                color: white;
                border: none;
                padding: 8px 16px;
                font-size: 14px;
                border-radius: 4px;
                min-width: 80px;
                transition: background-color 0.3s, transform 0.2s;
            }
            QPushButton:hover {
                background-color: #357abD;
                transform: translateY(-1px);
            }
            QPushButton:pressed {
                background-color: #2a65a0;
                transform: translateY(1px);
            }
            QLineEdit, QListWidget {
                border: 1px solid #d1d5db;
                border-radius: 4px;
                padding: 5px;
                font-size: 14px;
                background: white;
            }
            QProgressBar {
                border: 1px solid #d1d5db;
                border-radius: 4px;
                background: white;
                height: 20px;
            }
            QProgressBar::chunk {
                background-color: #4a90e2;
                border-radius: 4px;
            }
            QListWidget::item {
                padding: 5px;
            }
                /* 列表项选中状态 */
            QListWidget::item:selected {
                background-color: #4a90e8;  /* 蓝色背景 */
                color: white;
                border-radius: 3px;
            }

            /* 悬停在选中项上的状态 */
            QListWidget::item:selected:hover {
                background-color: #3a7bd5;  /* 稍深的蓝色 */
                color: white;
            }

            /* 普通悬停状态（未选中项） */
            QListWidget::item:hover {
                background-color: #e3f2fd;  /* 浅蓝色 */
            }

            /* 垂直滚动条样式 */
            QScrollBar:vertical {
                background: #f1f1f1;
                width: 12px;
                margin: 0px;
                border-radius: 6px;
            }
            QScrollBar::handle:vertical {
                background: #c1c1c1;
                min-height: 20px;
                border-radius: 6px;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                background: none;
                height: 0px;
            }
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
                background: none;
            }

            /* 水平滚动条样式 */
            QScrollBar:horizontal {
                background: #f1f1f1;
                height: 12px;
                margin: 0px;
                border-radius: 6px;
            }
            QScrollBar::handle:horizontal {
                background: #c1c1c1;
                min-width: 20px;
                border-radius: 6px;
            }
            QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
                background: none;
                width: 0px;
            }
            QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
                background: none;
            }
        """)

        # 初始化数据
        self.songs_list = []
        self.selected_song = None
        self.settings = QSettings("MonsterSirenDownloader", "Settings")
        self.last_download_path = self.settings.value("last_download_path", os.path.expanduser("~/Music"))

        # 创建UI组件
        self.create_widgets()
        self.setup_layout()

    def create_widgets(self):
        # 标题标签
        self.title_label = QLabel("MONSTER SIREN 音乐下载器")
        self.title_label.setFont(QFont("微软雅黑", 20, QFont.Bold))
        self.title_label.setAlignment(Qt.AlignCenter)
        self.title_label.setStyleSheet("color: #2d3748; margin-bottom: 15px;")
        self.title_opacity = QGraphicsOpacityEffect(self.title_label)
        self.title_opacity.setOpacity(0)
        self.title_label.setGraphicsEffect(self.title_opacity)

        # 搜索框
        self.search_entry = QLineEdit()
        self.search_entry.setPlaceholderText("搜索歌曲...")
        self.search_entry.setClearButtonEnabled(True)
        self.search_opacity = QGraphicsOpacityEffect(self.search_entry)
        self.search_opacity.setOpacity(0)
        self.search_entry.setGraphicsEffect(self.search_opacity)

        # 歌曲列表
        self.song_listbox = QListWidget()
        self.song_listbox.setAlternatingRowColors(True)
        self.song_listbox.setSpacing(2)
        self.list_opacity = QGraphicsOpacityEffect(self.song_listbox)
        self.list_opacity.setOpacity(0)
        self.song_listbox.setGraphicsEffect(self.list_opacity)

        # 歌曲信息
        self.song_info_label = QLabel()
        self.song_info_label.setWordWrap(True)
        self.song_info_label.setStyleSheet("""
            background-color: white;
            border: 1px solid #d1d5db;
            border-radius: 4px;
            padding: 12px;
            margin: 5px 0;
        """)
        self.info_opacity = QGraphicsOpacityEffect(self.song_info_label)
        self.info_opacity.setOpacity(0)
        self.song_info_label.setGraphicsEffect(self.info_opacity)

        # 路径选择
        self.path_entry = QLineEdit(self.last_download_path)
        self.browse_button = QPushButton("浏览")
        self.path_opacity = QGraphicsOpacityEffect(self.path_entry)
        self.path_opacity.setOpacity(0)
        self.path_entry.setGraphicsEffect(self.path_opacity)
        self.browse_opacity = QGraphicsOpacityEffect(self.browse_button)
        self.browse_opacity.setOpacity(0)
        self.browse_button.setGraphicsEffect(self.browse_opacity)

        # 操作按钮
        self.download_button = QPushButton("下载")
        self.load_button = QPushButton("刷新列表")
        self.download_opacity = QGraphicsOpacityEffect(self.download_button)
        self.download_opacity.setOpacity(0)
        self.download_button.setGraphicsEffect(self.download_opacity)
        self.load_opacity = QGraphicsOpacityEffect(self.load_button)
        self.load_opacity.setOpacity(0)
        self.load_button.setGraphicsEffect(self.load_opacity)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setTextVisible(True)
        self.progress_bar.setAlignment(Qt.AlignCenter)
        self.progress_bar.setRange(0, 100)
        self.progress_opacity = QGraphicsOpacityEffect(self.progress_bar)
        self.progress_opacity.setOpacity(0)
        self.progress_bar.setGraphicsEffect(self.progress_opacity)

    def setup_layout(self):
        # 搜索框布局
        search_layout = QHBoxLayout()
        search_layout.addWidget(self.search_entry)

        # 路径选择布局
        path_layout = QHBoxLayout()
        path_layout.addWidget(QLabel("下载路径:"))
        path_layout.addWidget(self.path_entry, 1)
        path_layout.addWidget(self.browse_button)

        # 按钮布局
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.download_button)
        button_layout.addWidget(self.load_button)

        # 主布局
        main_layout = QVBoxLayout()
        main_layout.setSpacing(10)
        main_layout.setContentsMargins(20, 20, 20, 20)
        main_layout.addWidget(self.title_label)
        main_layout.addLayout(search_layout)
        main_layout.addWidget(self.song_listbox, 1)
        main_layout.addWidget(self.song_info_label)
        main_layout.addLayout(path_layout)
        main_layout.addLayout(button_layout)
        main_layout.addWidget(self.progress_bar)

        container = QWidget()
        container.setLayout(main_layout)
        self.setCentralWidget(container)

    def setup_animations(self):
        # 窗口显示时的入场动画序列
        self.entrance_animations = QParallelAnimationGroup()

        # 标题淡入
        title_anim = QPropertyAnimation(self.title_opacity, b"opacity")
        title_anim.setDuration(800)
        title_anim.setStartValue(0)
        title_anim.setEndValue(1)
        title_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(title_anim)

        # 搜索框淡入
        search_anim = QPropertyAnimation(self.search_opacity, b"opacity")
        search_anim.setDuration(600)
        search_anim.setStartValue(0)
        search_anim.setEndValue(1)
        search_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(search_anim)

        # 列表淡入
        list_anim = QPropertyAnimation(self.list_opacity, b"opacity")
        list_anim.setDuration(700)
        list_anim.setStartValue(0)
        list_anim.setEndValue(1)
        list_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(list_anim)

        # 信息面板淡入
        info_anim = QPropertyAnimation(self.info_opacity, b"opacity")
        info_anim.setDuration(500)
        info_anim.setStartValue(0)
        info_anim.setEndValue(1)
        info_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(info_anim)

        # 路径选择淡入
        path_anim = QPropertyAnimation(self.path_opacity, b"opacity")
        path_anim.setDuration(400)
        path_anim.setStartValue(0)
        path_anim.setEndValue(1)
        path_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(path_anim)

        # 浏览按钮淡入
        browse_anim = QPropertyAnimation(self.browse_opacity, b"opacity")
        browse_anim.setDuration(400)
        browse_anim.setStartValue(0)
        browse_anim.setEndValue(1)
        browse_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(browse_anim)

        # 下载按钮淡入
        download_anim = QPropertyAnimation(self.download_opacity, b"opacity")
        download_anim.setDuration(300)
        download_anim.setStartValue(0)
        download_anim.setEndValue(1)
        download_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(download_anim)

        # 刷新按钮淡入
        load_anim = QPropertyAnimation(self.load_opacity, b"opacity")
        load_anim.setDuration(300)
        load_anim.setStartValue(0)
        load_anim.setEndValue(1)
        load_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(load_anim)

        # 进度条淡入
        progress_anim = QPropertyAnimation(self.progress_opacity, b"opacity")
        progress_anim.setDuration(200)
        progress_anim.setStartValue(0)
        progress_anim.setEndValue(1)
        progress_anim.setEasingCurve(QEasingCurve.OutQuad)
        self.entrance_animations.addAnimation(progress_anim)

    def setup_connections(self):
        self.load_button.clicked.connect(
            lambda: self.msr_json("https://monster-siren.hypergryph.com/api/songs", True)
        )
        self.search_entry.textChanged.connect(self.search_songs)
        self.song_listbox.itemSelectionChanged.connect(self.get_message)
        self.browse_button.clicked.connect(self.get_path)
        self.download_button.clicked.connect(self.download)

    def save_cache_to_disk(self):
        with open("cache.dat", "wb") as f:
            pickle.dump({
                'data': self.songs_cache,
                'time': self.last_fetch_time
            }, f)

    def load_cache_from_disk(self):
        try:
            with open("cache.dat", "rb") as f:
                cache = pickle.load(f)
                if time.time() - cache['time'] < self.cache_expire_time:
                    self.songs_cache = cache['data']
                    self.last_fetch_time = cache['time']
        except:
            pass

    def showEvent(self, event):
        super().showEvent(event)
        self.entrance_animations.start()

    def msr_json(self, url, force_refresh=False):
        """带缓存功能的网络请求"""
        current_time = time.time()

        # 检查缓存是否有效
        if not force_refresh and self.songs_cache and \
                (current_time - self.last_fetch_time) < self.cache_expire_time:
            self.songs_list = self.songs_cache
            self.update_song_list()
            return

        # 显示加载状态
        self.progress_bar.setRange(0, 0)
        self.set_ui_enabled(False)

        # 创建并启动网络工作线程
        self.worker = NetworkWorker(url, force_refresh)
        self.worker.signals.data_loaded.connect(self.on_data_loaded)
        self.worker.signals.error.connect(self.on_network_error)
        self.worker.signals.finished.connect(self.on_worker_finished)
        self.worker.start()

    def on_data_loaded(self, data):
        self.songs_cache = data
        self.last_fetch_time = time.time()
        self.songs_list = data
        self.update_song_list()
        self.save_cache_to_disk()

    def on_network_error(self, error_msg):
        if self.songs_cache:
            self.show_message("警告", f"使用缓存数据\n{error_msg}")
            self.songs_list = self.songs_cache
            self.update_song_list()
        else:
            self.show_message("错误", f"无缓存且网络连接失败\n{error_msg}")

    def on_worker_finished(self):
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        self.set_ui_enabled(True)

    def update_song_list(self):
        """更新歌曲列表显示"""
        self.song_listbox.clear()
        if not self.songs_list:
            self.song_listbox.addItem("无歌曲数据")
            return

        # 添加排序和图标等增强功能
        sorted_songs = sorted(self.songs_list, key=lambda x: x["name"])
        for song in sorted_songs:
            item = QListWidgetItem(song["name"])
            item.setData(Qt.UserRole, {  # 存储完整数据
                'cid': song['cid'],
                'artists': song['artists'],
                'albumCid': song['albumCid']
            })
            self.song_listbox.addItem(item)

    def search_songs(self):
        search_query = self.search_entry.text().strip().lower()
        self.song_listbox.clear()
        for song in self.songs_list:
            if search_query in song["name"].lower():
                item = QListWidgetItem(song["name"])
                item.setData(Qt.UserRole, {
                    'cid': song['cid'],
                    'artists': song['artists'],
                    'albumCid': song['albumCid']
                })
                self.song_listbox.addItem(item)
        self.selected_song = None  # 搜索后重置选择

    def get_message(self):
        selected_items = self.song_listbox.selectedItems()
        if selected_items:
            item = selected_items[0]
            song_data = item.data(Qt.UserRole)
            self.selected_song = {  # 更新selected_song
                'name': item.text(),
                'cid': song_data['cid'],
                'artists': song_data['artists'],
                'albumCid': song_data['albumCid']
            }
            info_text = (f"<b>歌曲名称:</b> {item.text()}<br>"
                         f"<b>艺术家:</b> {', '.join(song_data['artists'])}<br>"
                         f"<b>专辑CID:</b> {song_data['albumCid']}<br>"
                         f"<b>歌曲CID:</b> {song_data['cid']}")
            self.song_info_label.setText(info_text)
        else:
            self.selected_song = None  # 清除选择
            self.song_info_label.setText("")

    def get_path(self):
        path = QFileDialog.getExistingDirectory(
            self,
            "选择下载路径",
            self.last_download_path,
            options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
        )
        if path:
            self.path_entry.setText(path)
            self.last_download_path = path
            self.settings.setValue("last_download_path", path)

    def download(self):
        if not self.selected_song:
            self.show_message("警告", "请先选择一首歌曲")
            return

        cid = self.selected_song["cid"]
        html_url = f"https://monster-siren.hypergryph.com/music/{cid}"

        # 禁用UI防止重复操作
        self.set_ui_enabled(False)
        self.progress_bar.setRange(0, 0)

        # 创建并启动URL获取线程
        self.url_fetch_worker = UrlFetchWorker(html_url)
        self.url_fetch_worker.signals.url_obtained.connect(self.on_url_obtained)
        self.url_fetch_worker.signals.error.connect(self.on_download_error)
        self.url_fetch_worker.signals.finished.connect(self.on_url_fetch_finished)
        self.url_fetch_worker.start()

    def on_url_obtained(self, down_url):
        # 确保下载路径存在
        download_dir = self.path_entry.text()
        if not os.path.exists(download_dir):
            os.makedirs(download_dir)

        # 处理文件名中的非法字符
        safe_name = re.sub(r'[\\/*?:"<>|]', "", self.selected_song["name"])
        save_path = os.path.join(download_dir, f"{safe_name}.mp3")

        # 检查文件是否已存在
        if os.path.exists(save_path):
            self.show_message("提示", "文件已存在，将重新下载")
            os.remove(save_path)

        # 重置进度条
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)

        # 创建并启动下载线程
        self.current_download_worker = DownloadWorker(down_url, save_path)
        self.current_download_worker.signals.download_progress.connect(self.on_download_progress)
        self.current_download_worker.signals.download_complete.connect(self.on_download_complete)
        self.current_download_worker.signals.error.connect(self.on_download_error)
        self.current_download_worker.signals.finished.connect(self.on_download_finished)
        self.current_download_worker.start()

    def on_download_progress(self, progress):
        if progress < 0:  # 负数表示设置最大值
            self.progress_bar.setMaximum(-progress)
        else:
            self.progress_bar.setValue(progress)

    def on_download_complete(self, save_path):
        self.show_message("成功", f"文件已成功下载到:\n{save_path}")

    def on_download_error(self, error_msg):
        self.show_message("错误", error_msg)

    def on_url_fetch_finished(self):
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)

    def on_download_finished(self):
        self.set_ui_enabled(True)

    def set_ui_enabled(self, enabled):
        """启用或禁用UI控件"""
        self.search_entry.setEnabled(enabled)
        self.song_listbox.setEnabled(enabled)
        self.path_entry.setEnabled(enabled)
        self.browse_button.setEnabled(enabled)
        self.download_button.setEnabled(enabled)
        self.load_button.setEnabled(enabled)

    def show_message(self, title, message):
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle(title)
        msg_box.setText(message)
        msg_box.exec()

    def closeEvent(self, event):
        # 确保所有线程在退出前完成
        if hasattr(self, 'worker') and self.worker.isRunning():
            self.worker.quit()
            self.worker.wait()
        if hasattr(self, 'current_download_worker') and self.current_download_worker and self.current_download_worker.isRunning():
            self.current_download_worker.requestInterruption()
            self.current_download_worker.wait()
        if hasattr(self, 'url_fetch_worker') and self.url_fetch_worker and self.url_fetch_worker.isRunning():
            self.url_fetch_worker.quit()
            self.url_fetch_worker.wait()
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")

    # 设置调色板
    palette = app.palette()
    palette.setColor(QPalette.Window, QColor("#f5f7fa"))
    palette.setColor(QPalette.WindowText, QColor("#2d3748"))
    palette.setColor(QPalette.Base, QColor("#ffffff"))
    palette.setColor(QPalette.AlternateBase, QColor("#f8fafc"))
    palette.setColor(QPalette.Button, QColor("#4a90e2"))
    palette.setColor(QPalette.ButtonText, QColor("#ffffff"))
    palette.setColor(QPalette.Highlight, QColor("#4a90e2"))
    palette.setColor(QPalette.HighlightedText, QColor("#ffffff"))
    app.setPalette(palette)

    window = MusicDownloader()
    window.show()
    sys.exit(app.exec())